In many computer programming languages, variables in a program have to be declared with a type, such as integer, character, float, and so forth, and thereafter the program needs to abide by type rules. Compiled computer languages that are type safe, such as the C++ programming language, check for type consistency at compile time. One way that type consistency is ensured is by requiring that arrays hold homogenous types of data. For example, if a programmer defines an array to hold integers, the compiler can check whether the program later commands that a non-integer type (e.g., character) be retrieved from that array. If so, the compiler generates an error, whereby that program code cannot be linked into an executable program, which is beneficial because such a type safety violation otherwise would cause instability and unpredictability in the executable program.
Such strongly-typed languages thus provide many benefits, but at the same time create difficulties and other problems for programmers. For example, consider a test framework that tests other programs with various parameters and test conditions, and thus generates various types of data. Because an array can be defined to hold only one type of data, in order to store arbitrary/generic (non-polymorphic) data in a standard data store, a programmer is left with only a few choices as to how to handle such data.
One way to store the data is to simply store it as is, by defining an array of void pointers, e.g., void * array [X], and then storing the data absent type, e.g., array[0]=(void*)&1. However, because the type of the store is void, such a storage technique forfeits type safety, as there is no way with such a generic store to know what any given element's data type is. Consequently, when compiled, any source code that retrieves that element from the array cannot be checked as to whether the element's specified data type when retrieved matches its data type when stored. For example, the element stored into the void pointer array may correspond to an integer, but if that element was retrieved from the array by a command that requested a character pointer, nothing in the compiler is able to detect the error. As can be readily appreciated, the program module will thus compile, but the resultant executable program likely will be highly unstable and/or yield unpredictable or incorrect results.
Other ways to store such arbitrary data include forcing these different types to belong to the same base class, and introducing an artificial design constraint to force polymorphism. As can be appreciated, this technique makes the program unnecessarily complex. Yet another way to handle arbitrary data storage is to have a closed list of types with an identifier (e.g., 0, 1 . . . n) for each type. Then, at run time, a (likely very large) switch statement, e.g., case [0] . . . case [1] . . . to case [n] . . . , can be used to return the appropriate typed variable. This suffers from the problem that the list of types is closed, and the switch statement needs to be programmed to handle every type.